package com.glidebitmappool.internal;

import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 位图池链式组
 *
 * @author renpengfei
 * @param <K>
 * @param <V>
 * @since 2021-03-12
 */
public class GroupedLinkedMap<K extends Poolable, V> {
    private static final int TWO = 2;

    private HiLogLabel logLabel = new HiLogLabel(HiLog.LOG_APP, 0x001, "GroupedLinkedMap--");

    private final LinkedEntry<K, V> head = new LinkedEntry<>();
    private final Map<K, LinkedEntry<K, V>> keyToEntry = new HashMap<>();

    /**
     * 往位图池添加数据
     *
     * @param key
     * @param value
     */
    public void put(K key, V value) {
        LinkedEntry<K, V> entry = keyToEntry.get(key);

        if (entry == null) {
            entry = new LinkedEntry<>(key);
            makeTail(entry);
            keyToEntry.put(key, entry);
        } else {
            key.offer();
        }

        entry.add(value);
    }

    /**
     * 根据key获取数据
     *
     * @param key
     * @return value
     */
    public V get(K key) {
        LinkedEntry<K, V> entry = keyToEntry.get(key);
        if (entry == null) {
            entry = new LinkedEntry<>(key);
            keyToEntry.put(key, entry);
        } else {
            key.offer();
        }

        HiLog.error(logLabel, "key---" + entry.key + "---values--"
                + entry.values + "--prev--" + entry.prev + "---next--" + entry.next);
        makeHead(entry);

        return entry.removeLast();
    }

    /**
     * 删除最后一条数据
     *
     * @return value
     */
    public V removeLast() {
        V removed = null;
        LinkedEntry<K, V> last = head.prev;

        while (!last.equals(head)) {
            removed = last.removeLast();
            if (removed != null) {
                return removed;
            } else {
                // We will clean up empty lru entries since they are likely to have been one off or
                // unusual sizes and
                // are not likely to be requested again so the gc thrash should be minimal. Doing so will
                // speed up our
                // removeLast operation in the future and prevent our linked list from growing to
                // arbitrarily large
                // sizes.
                removeEntry(last);
                keyToEntry.remove(last.key);
                last.key.offer();
            }

            last = last.prev;
        }

        return removed;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("GroupedLinkedMap( ");
        LinkedEntry<K, V> current = head.next;
        boolean isHadAtLeastOneItem = false;
        while (!current.equals(head)) {
            isHadAtLeastOneItem = true;
            sb.append('{').append(current.key).append(':').append(current.size()).append("}, ");
            current = current.next;
        }
        if (isHadAtLeastOneItem) {
            sb.delete(sb.length() - TWO, sb.length());
        }
        return sb.append(" )").toString();
    }

    /**
     * Make the entry the most recently used item.
     *
     * @param entry
     */
    private void makeHead(LinkedEntry<K, V> entry) {
        removeEntry(entry);
        entry.prev = head;
        entry.next = head.next;
        updateEntry(entry);
    }

    /**
     * Make the entry the least recently used item.
     *
     * @param entry
     */
    private void makeTail(LinkedEntry<K, V> entry) {
        removeEntry(entry);
        entry.prev = head.prev;
        entry.next = head;
        updateEntry(entry);
    }

    private static <K, V> void updateEntry(LinkedEntry<K, V> entry) {
        entry.next.prev = entry;
        entry.prev.next = entry;
    }

    private static <K, V> void removeEntry(LinkedEntry<K, V> entry) {
        entry.prev.next = entry.next;
        entry.next.prev = entry.prev;
    }

    /**
     * 链式
     *
     * @param <K>
     * @param <V>
     * @since2021-03-12
     */
    private static class LinkedEntry<K, V> {
        private LinkedEntry<K, V> next;
        private LinkedEntry<K, V> prev;
        private final K key;
        private List<V> values;

        /**
         * Used only for the first item in the list which we will treat specially and which will not contain value.
         */
        LinkedEntry() {
            this(null);
        }

        LinkedEntry(K key) {
            next = prev;
            prev = this;
            next = this;
            this.key = key;
        }

        V removeLast() {
            final int valueSize = size();
            return valueSize > 0 ? values.remove(valueSize - 1) : null;
        }

        int size() {
            return values != null ? values.size() : 0;
        }

        void add(V value) {
            if (values == null) {
                values = new ArrayList<>();
            }
            values.add(value);
        }
    }
}

